

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <title>表单校验 — Netinfo.Vue.js</title>
    <meta charset="utf-8">
    <meta name="description" content="Vue.js - The Progressive JavaScript Framework">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

    <link rel="alternate" hreflang="x-default" href="https://vuejs.org/v2/cookbook/form-validation.html">
    <link rel="alternate" hreflang="zh" href="https://cn.vuejs.org/v2/cookbook/form-validation.html">
    <link rel="alternate" hreflang="ja" href="https://jp.vuejs.org/v2/cookbook/form-validation.html">
    <link rel="alternate" hreflang="ru" href="https://ru.vuejs.org/v2/cookbook/form-validation.html">
    <link rel="alternate" hreflang="ko" href="https://kr.vuejs.org/v2/cookbook/form-validation.html">
    <link rel="alternate" hreflang="pt-BR" href="https://br.vuejs.org/v2/cookbook/form-validation.html">
    <link rel="alternate" hreflang="fr" href="https://fr.vuejs.org/v2/cookbook/form-validation.html">

    <meta property="og:type" content="article">
    <meta property="og:title" content="表单校验 — Vue.js">
    <meta property="og:description" content="Vue.js - The Progressive JavaScript Framework">
    <meta property="og:image" content="https://cn.vuejs.org//netinfofe/images/logo.png">

    <meta name="twitter:card" content="summary">
    <meta name="twitter:title" content="表单校验 — Vue.js">
    <meta name="twitter:description" content="Vue.js - The Progressive JavaScript Framework">
    <meta name="twitter:image" content="https://cn.vuejs.org/images/logo.png">

    <link rel="apple-touch-icon" sizes="57x57" href="/netinfofe/images/icons/apple-icon-57x57.png">
    <link rel="apple-touch-icon" sizes="60x60" href="/netinfofe/images/icons/apple-icon-60x60.png">
    <link rel="apple-touch-icon" sizes="72x72" href="/netinfofe/images/icons/apple-icon-72x72.png">
    <link rel="apple-touch-icon" sizes="76x76" href="/netinfofe/images/icons/apple-icon-76x76.png">
    <link rel="apple-touch-icon" sizes="114x114" href="/netinfofe/images/icons/apple-icon-114x114.png">
    <link rel="apple-touch-icon" sizes="120x120" href="/netinfofe/images/icons/apple-icon-120x120.png">
    <link rel="apple-touch-icon" sizes="144x144" href="/netinfofe/images/icons/apple-icon-144x144.png">
    <link rel="apple-touch-icon" sizes="152x152" href="/netinfofe/images/icons/apple-icon-152x152.png">
    <link rel="apple-touch-icon" sizes="180x180" href="/netinfofe/images/icons/apple-icon-180x180.png">
    <link rel="icon" type="image/png" sizes="192x192"  href="/netinfofe/images/icons/android-icon-192x192.png">
    <link rel="icon" type="image/png" sizes="32x32" href="/netinfofe/images/icons/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="96x96" href="/netinfofe/images/icons/favicon-96x96.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/netinfofe/images/icons/favicon-16x16.png">
    <meta name="msapplication-TileImage" content="/images/icons/ms-icon-144x144.png">
    <link rel="icon" href="/netinfofe/images/logo.png" type="image/png">

    <meta name="msapplication-TileColor" content="#4fc08d">
    <meta name="theme-color" content="#4fc08d">

    <meta name="msapplication-config" content="browserconfig.xml">
    <link rel="manifest" href="/manifest.json">

    <!-- <link href='//fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600|Roboto Mono' rel='stylesheet' type='text/css'> -->
    <!-- <link href='//fonts.googleapis.com/css?family=Dosis:500&text=Vue.js' rel='stylesheet' type='text/css'> -->

    <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">

    <!-- main page styles -->
    <link rel="stylesheet" href="/netinfofe/css/page.css">

    <!-- this needs to be loaded before guide's inline scripts -->
    <script src="/netinfofe/js/vue.js"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <script>
      Vue.config.productionTip = true
      window.PAGE_TYPE = "cookbook"
    </script>

    <!-- ga -->
    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

      ga('create', 'UA-46852172-3', 'cn.vuejs.org');
      ga('send', 'pageview');
    </script>
  </head>
  <body class="docs">
    <div id="mobile-bar" >
      <a class="menu-button"></a>
      <a class="logo" href="/"></a>
    </div>
    <div id="header">
  <a id="logo" href="/netinfofe/">
    <img src="/netinfofe/images/logo.png" alt="vue logo">
    <span>Netinfo.Vue.js</span>
  </a>
  <ul id="nav">
    <li>
  <!--<form id="search-form">
    <input type="text" id="search-query-nav" class="search-query st-default-search-input">
  </form>-->
</li>
<!--<li class="nav-dropdown-container learn">
  <a class="nav-link current">学习</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><ul>
      <li><a href="/netinfofe/v2/guide/" class="nav-link">教程</a></li>
      <li><a href="/netinfofe/v2/api/" class="nav-link">API</a></li>
      <li><a href="/netinfofe/v2/style-guide/" class="nav-link">风格指南</a></li>
      <li><a href="/netinfofe/v2/examples/" class="nav-link">示例</a></li>
      <li><a href="/netinfofe/v2/cookbook/" class="nav-link current">Cookbook</a></li>
    </ul></li>
  </ul>
</li>
-->
<!--<li class="nav-dropdown-container ecosystem">
  <a class="nav-link">生态系统</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><h4>帮助</h4></li>
    <li><ul>
      <li><a href="https://forum.vuejs.org/" class="nav-link" target="_blank">论坛</a></li>
      <li><a href="https://chat.vuejs.org/" class="nav-link" target="_blank">聊天室</a></li>
      <li><a href="https://www.vuemeetups.org/" class="nav-link" target="_blank">聚会</a></li>
    </ul></li>
    <li><h4>工具</h4></li>
    <li>
      <ul>
        <li><a href="https://github.com/vuejs/vue-devtools" class="nav-link" target="_blank">Devtools</a></li>
        <li><a href="https://cli.vuejs.org/" class="nav-link" target="_blank">Vue CLI</a></li>
        <li><a href="https://vue-loader.vuejs.org" class="nav-link" target="_blank">Vue Loader</a></li>
      </ul>
    </li>
    <li><h4>核心插件</h4></li>
    <li><ul>
      <li><a href="https://router.vuejs.org/" class="nav-link" target="_blank">Vue Router</a></li>
      <li><a href="https://vuex.vuejs.org/" class="nav-link" target="_blank">Vuex</a></li>
      <li><a href="https://ssr.vuejs.org/" class="nav-link" target="_blank">Vue 服务端渲染</a></li>
    </ul></li>
    <li><h4>信息</h4></li>
    <li><ul>
      <li><a href="https://news.vuejs.org" class="nav-link" target="_blank">周刊</a></li>
      <li><a href="https://github.com/vuejs/roadmap" class="nav-link" target="_blank">Roadmap</a></li>
      <li><a href="https://twitter.com/vuejs" class="nav-link" target="_blank">Twitter</a></li>
      <li><a href="https://medium.com/the-vue-point" class="nav-link" target="_blank">博客</a></li>
      <li><a href="https://vuejobs.com/?ref=vuejs" class="nav-link" target="_blank">工作</a></li>
    </ul></li>
    <li><h4>资源列表</h4></li>
    <li><ul>
      <li><a href="https://github.com/vuejs" class="nav-link" target="_blank">官方仓库</a></li>
      <li><a href="https://curated.vuejs.org/" class="nav-link" target="_blank">Vue 优选</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" class="nav-link" target="_blank">Vue 资源</a></li>
    </ul></li>
  </ul>
</li>
-->

<!--<li class="nav-dropdown-container support-vue">
  <a href="/support-vuejs/" class="nav-link">支持 Vue</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><ul>
      <li><a href="/support-vuejs/#One-time-Donations" class="nav-link">一次性赞助</a></li>
      <li><a href="/support-vuejs/#Recurring-Pledges" class="nav-link">周期性赞助</a></li>
      <li><a href="https://www.smallsticker.com/%E8%B4%B4%E7%BA%B8/vue.html" class="nav-link">贴纸</a></li>
      <li><a href="https://vue.threadless.com" target="_blank" class="nav-link">T 恤商店</a></li>
    </ul></li>
  </ul>
</li>
-->
<!--<li class="nav-dropdown-container language">
  <a class="nav-link">多语言</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><a href="https://vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">English</a></li>
    <li><a href="https://jp.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">日本語</a></li>
    <li><a href="https://ru.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Русский</a></li>
    <li><a href="https://kr.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">한국어</a></li>
    <li><a href="https://br.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Português</a></li>
    <li><a href="https://fr.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Français</a></li>
    <li><a href="https://vi.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Tiếng Việt</a></li>
  </ul>
</li>
-->
<!--<li><a href="https://github.com/vuejs/cn.vuejs.org/" target="_blank" class="nav-link contribute">参与翻译</a></li>-->

<li><a href="/netinfofe/v2/guide/" class="nav-link">教程</a></li>
<li><a href="/netinfofe/v2/components/" class="nav-link">组件</a></li>
<li><a href="/netinfofe/v2/api/" class="nav-link">API</a></li>
<li><a href="/netinfofe/v2/style-guide/" class="nav-link">风格指南</a></li>
<li><a href="/netinfofe/v2/examples/" class="nav-link">示例</a></li>
<li><a href="/netinfofe/v2/cookbook/" class="nav-link current">Cookbook</a></li>


<li>
  <a href="/netinfofe/v2/aboutus/" class="nav-link team">关于我们</a>
</li>

  </ul>
</div>

    
      <div id="main" class="fix-sidebar">
        
          
  <div class="sidebar">
  <div class="sidebar-inner">
    <ul class="main-menu">
      <li>
  <!--<form id="search-form">
    <input type="text" id="search-query-sidebar" class="search-query st-default-search-input">
  </form>-->
</li>
<!--<li class="nav-dropdown-container learn">
  <a class="nav-link current">学习</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><ul>
      <li><a href="/netinfofe/v2/guide/" class="nav-link">教程</a></li>
      <li><a href="/netinfofe/v2/api/" class="nav-link">API</a></li>
      <li><a href="/netinfofe/v2/style-guide/" class="nav-link">风格指南</a></li>
      <li><a href="/netinfofe/v2/examples/" class="nav-link">示例</a></li>
      <li><a href="/netinfofe/v2/cookbook/" class="nav-link current">Cookbook</a></li>
    </ul></li>
  </ul>
</li>
-->
<!--<li class="nav-dropdown-container ecosystem">
  <a class="nav-link">生态系统</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><h4>帮助</h4></li>
    <li><ul>
      <li><a href="https://forum.vuejs.org/" class="nav-link" target="_blank">论坛</a></li>
      <li><a href="https://chat.vuejs.org/" class="nav-link" target="_blank">聊天室</a></li>
      <li><a href="https://www.vuemeetups.org/" class="nav-link" target="_blank">聚会</a></li>
    </ul></li>
    <li><h4>工具</h4></li>
    <li>
      <ul>
        <li><a href="https://github.com/vuejs/vue-devtools" class="nav-link" target="_blank">Devtools</a></li>
        <li><a href="https://cli.vuejs.org/" class="nav-link" target="_blank">Vue CLI</a></li>
        <li><a href="https://vue-loader.vuejs.org" class="nav-link" target="_blank">Vue Loader</a></li>
      </ul>
    </li>
    <li><h4>核心插件</h4></li>
    <li><ul>
      <li><a href="https://router.vuejs.org/" class="nav-link" target="_blank">Vue Router</a></li>
      <li><a href="https://vuex.vuejs.org/" class="nav-link" target="_blank">Vuex</a></li>
      <li><a href="https://ssr.vuejs.org/" class="nav-link" target="_blank">Vue 服务端渲染</a></li>
    </ul></li>
    <li><h4>信息</h4></li>
    <li><ul>
      <li><a href="https://news.vuejs.org" class="nav-link" target="_blank">周刊</a></li>
      <li><a href="https://github.com/vuejs/roadmap" class="nav-link" target="_blank">Roadmap</a></li>
      <li><a href="https://twitter.com/vuejs" class="nav-link" target="_blank">Twitter</a></li>
      <li><a href="https://medium.com/the-vue-point" class="nav-link" target="_blank">博客</a></li>
      <li><a href="https://vuejobs.com/?ref=vuejs" class="nav-link" target="_blank">工作</a></li>
    </ul></li>
    <li><h4>资源列表</h4></li>
    <li><ul>
      <li><a href="https://github.com/vuejs" class="nav-link" target="_blank">官方仓库</a></li>
      <li><a href="https://curated.vuejs.org/" class="nav-link" target="_blank">Vue 优选</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" class="nav-link" target="_blank">Vue 资源</a></li>
    </ul></li>
  </ul>
</li>
-->

<!--<li class="nav-dropdown-container support-vue">
  <a href="/support-vuejs/" class="nav-link">支持 Vue</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><ul>
      <li><a href="/support-vuejs/#One-time-Donations" class="nav-link">一次性赞助</a></li>
      <li><a href="/support-vuejs/#Recurring-Pledges" class="nav-link">周期性赞助</a></li>
      <li><a href="https://www.smallsticker.com/%E8%B4%B4%E7%BA%B8/vue.html" class="nav-link">贴纸</a></li>
      <li><a href="https://vue.threadless.com" target="_blank" class="nav-link">T 恤商店</a></li>
    </ul></li>
  </ul>
</li>
-->
<!--<li class="nav-dropdown-container language">
  <a class="nav-link">多语言</a><span class="arrow"></span>
  <ul class="nav-dropdown">
    <li><a href="https://vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">English</a></li>
    <li><a href="https://jp.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">日本語</a></li>
    <li><a href="https://ru.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Русский</a></li>
    <li><a href="https://kr.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">한국어</a></li>
    <li><a href="https://br.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Português</a></li>
    <li><a href="https://fr.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Français</a></li>
    <li><a href="https://vi.vuejs.org/v2/cookbook/form-validation.html" class="nav-link" target="_blank">Tiếng Việt</a></li>
  </ul>
</li>
-->
<!--<li><a href="https://github.com/vuejs/cn.vuejs.org/" target="_blank" class="nav-link contribute">参与翻译</a></li>-->

<li><a href="/netinfofe/v2/guide/" class="nav-link">教程</a></li>
<li><a href="/netinfofe/v2/components/" class="nav-link">组件</a></li>
<li><a href="/netinfofe/v2/api/" class="nav-link">API</a></li>
<li><a href="/netinfofe/v2/style-guide/" class="nav-link">风格指南</a></li>
<li><a href="/netinfofe/v2/examples/" class="nav-link">示例</a></li>
<li><a href="/netinfofe/v2/cookbook/" class="nav-link current">Cookbook</a></li>


<li>
  <a href="/netinfofe/v2/aboutus/" class="nav-link team">关于我们</a>
</li>

    </ul>
    <div class="list">
      
        <h2>
          
          Cookbook<sup class="beta">beta</sup>

        </h2>
        <ul class="menu-root">
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/index.html" class="sidebar-link">介绍</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/adding-instance-properties.html" class="sidebar-link">添加实例属性</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/form-validation.html" class="sidebar-link current">表单校验</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/editable-svg-icons.html" class="sidebar-link">可编辑的 SVG 图标系统</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/serverless-blog.html" class="sidebar-link">创建一个基于 CMS 的博客</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/unit-testing-vue-components.html" class="sidebar-link">Vue 组件的单元测试</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/creating-custom-scroll-directives.html" class="sidebar-link">创建自定义滚动指令</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/debugging-in-vscode.html" class="sidebar-link">在 VS Code 和 Chrome 中调试</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/using-axios-to-consume-apis.html" class="sidebar-link">使用 axios 访问 API</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/avoiding-memory-leaks.html" class="sidebar-link">避免内存泄漏</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/client-side-storage.html" class="sidebar-link">Client-Side Storage</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/packaging-sfc-for-npm.html" class="sidebar-link">Packaging Vue Components for npm</a>
    </li>
  
    
    
    <li>
      <a href="/netinfofe/v2/cookbook/dockerize-vuejs-app.html" class="sidebar-link">Dockerize Vue.js App</a>
    </li>
  
</ul>

      
    </div>
  </div>
</div>

<!--
<div id="sidebar-sponsors-platinum-right">
  <div class="main-sponsor">
    <span>白金赞助商</span>
    <div>
    <a href="https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue" target="_blank" class="logo">
      <img src="/netinfofe/images/bit-wide.png" alt="">
    </a>
    <a href="http://tooltwist.com/" target="_blank" class="logo">
      <img src="/netinfofe/images/tooltwist.png" alt="tooltwist">
    </a>
    <a href="https://vueschool.io/?utm_source=Vuejs.org&utm_medium=Banner&utm_campaign=Sponsored%20Banner&utm_content=V1" target="_blank" class="logo">
      <img src="/netinfofe/images/vueschool.png" alt="vue school">
    </a>
    <a href="https://vehikl.com/" target="_blank" class="logo">
      <img src="/netinfofe/images/vehikl.png" alt="vehikl">
    </a>
    <a href="https://www.nativescript.org/vue?utm_source=vue-js-org&utm_medium=website&utm_campaign=nativescript-awareness" target="_blank" class="logo">
      <img src="/netinfofe/images/nativescript.png" alt="nativescript">
    </a>
    </div>
  </div>
  <a class="become-backer" href="/netinfofe/support-vuejs">
    成为赞助者
  </a>
</div>

-->


<div class="content cookbook with-sidebar ">
  
    
      <div id="ad">
  <!--<a href="https://time.geekbang.org/column/intro/81?utm_source=website&utm_medium=vue&utm_campaign=81-onsell&utm_content=201805banner" target="_blank" rel="nofollow">
    <img src="/images/geekbang5.gif" alt="极客时间">
  </a>-->
</div>

    
  
  
    <h1>表单校验</h1>
  
  
    <h2 id="基本的示例"><a href="#基本的示例" class="headerlink" title="基本的示例"></a>基本的示例</h2><p>表单校验是浏览器原生支持的，但是有的时候用不同的浏览器处理起来需要一些小技巧。即使当表单校验已经被完美支持，你也还是有很多时候需要进行自定义的校验。这时一个更加手动的基于 Vue 的解决方案可能会更适合。我们来看一个简单的示例。</p>
<p>给定一个表单，包含三个字段，其中两个是必填项。我们先来看看 HTML：</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span></span></span><br><span class="line"><span class="tag">  <span class="attr">id</span>=<span class="string">"app"</span></span></span><br><span class="line"><span class="tag">  @<span class="attr">submit</span>=<span class="string">"checkForm"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">action</span>=<span class="string">"https://vuejs.org/"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">method</span>=<span class="string">"post"</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"errors.length"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">b</span>&gt;</span>Please correct the following error(s):<span class="tag">&lt;/<span class="name">b</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ul</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">li</span> <span class="attr">v-for</span>=<span class="string">"error in errors"</span>&gt;</span>&#123;&#123; error &#125;&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"name"</span>&gt;</span>Name<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">id</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"text"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"age"</span>&gt;</span>Age<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">id</span>=<span class="string">"age"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">"age"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"number"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"age"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">min</span>=<span class="string">"0"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"movie"</span>&gt;</span>Favorite Movie<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span></span></span><br><span class="line"><span class="tag">      <span class="attr">id</span>=<span class="string">"movie"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">"movie"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"movie"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">option</span>&gt;</span>Star Wars<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">option</span>&gt;</span>Vanilla Sky<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">option</span>&gt;</span>Atomic Blonde<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> <span class="attr">value</span>=<span class="string">"Submit"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>我们从头到尾看一遍，这个 <code>&lt;form&gt;</code> 标记上有一个我们将会用在 Vue 组件上的 ID。这里有一个你稍后会看到的 <code>submit</code> 处理函数，而这里的 <code>action</code> 是一个可能指向了某个真实服务器的临时 URL (当然你在服务端也要是有校验的)。</p>
<p>下面有一段内容，会根据错误状态进行显示或隐藏。它将会在表单的最顶端渲染一个简单的错误列表。同时要注意我们会在提交的时候进行校验，而不是每个字段被修改的时候。</p>
<p>最后值得注意的是这三个字段都有一个对应的 <code>v-model</code> 来连接它们的值，我们将会在 JavaScript 中使用它。现在我们就来看一下。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  el: <span class="string">'#app'</span>,</span><br><span class="line">  data: &#123;</span><br><span class="line">    errors: [],</span><br><span class="line">    name: <span class="literal">null</span>,</span><br><span class="line">    age: <span class="literal">null</span>,</span><br><span class="line">    movie: <span class="literal">null</span></span><br><span class="line">  &#125;,</span><br><span class="line">  methods:&#123;</span><br><span class="line">    checkForm: <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.name &amp;&amp; <span class="keyword">this</span>.age) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">this</span>.errors = [];</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.name) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errors.push(<span class="string">'Name required.'</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.age) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errors.push(<span class="string">'Age required.'</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      e.preventDefault();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>非常短小精悍。我们定义了一个数组来放置错误，并将这三个表单字段的默认值设为 <code>null</code>。<code>checkForm</code> 的逻辑 (在表单提交时运行) 只会检查姓名和年龄，因为电影时选填的。如果它们是空的，那么我们会检查每一个字段并设置相应的错误，差不多就是这样。你可以在下面运行这个 demo。不要忘记提交成功时它会 POST 到一个临时的 URL。</p>
<p></p><p data-height="265" data-theme-id="0" data-slug-hash="GObpZM" data-default-tab="html,result" data-user="cfjedimaster" data-embed-version="2" data-pen-title="form validation 1" class="codepen">在 <a href="https://codepen.io" target="_blank" rel="noopener">CodePen</a> 查看 Raymond Camden (<a href="https://codepen.io/cfjedimaster" target="_blank" rel="noopener">@cfjedimaster</a>) 的 <a href="https://codepen.io/cfjedimaster/pen/GObpZM/" target="_blank" rel="noopener">表单校验 1</a>。</p><p></p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

<h2 id="使用自定义校验"><a href="#使用自定义校验" class="headerlink" title="使用自定义校验"></a>使用自定义校验</h2><p>对于第二个示例来说，第二个文本字段 (年龄) 变换成了电子邮件地址，它将会通过一些自定义的逻辑来校验。这部分代码来自 StackOverflow 的问题：<a href="https://stackoverflow.com/questions/46155/how-to-validate-email-address-in-javascript" target="_blank" rel="noopener">如何在 JavaScript 中校验电子邮件地址</a>。这是一个很好的问题，因为它会让 Facebook 上最激烈的政治、宗教争论看上去都只是“哪家的啤酒最好喝”这样的小分歧了。讲真的这很疯狂。我们来看 HTML，尽管它和第一个例子很接近。</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span></span></span><br><span class="line"><span class="tag">  <span class="attr">id</span>=<span class="string">"app"</span></span></span><br><span class="line"><span class="tag">  @<span class="attr">submit</span>=<span class="string">"checkForm"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">action</span>=<span class="string">"https://vuejs.org/"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">method</span>=<span class="string">"post"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">novalidate</span>=<span class="string">"true"</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"errors.length"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">b</span>&gt;</span>Please correct the following error(s):<span class="tag">&lt;/<span class="name">b</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ul</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">li</span> <span class="attr">v-for</span>=<span class="string">"error in errors"</span>&gt;</span>&#123;&#123; error &#125;&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"name"</span>&gt;</span>Name<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">id</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"text"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"email"</span>&gt;</span>Email<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">id</span>=<span class="string">"email"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">"email"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"email"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"email"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"movie"</span>&gt;</span>Favorite Movie<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span></span></span><br><span class="line"><span class="tag">      <span class="attr">id</span>=<span class="string">"movie"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">"movie"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"movie"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">option</span>&gt;</span>Star Wars<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">option</span>&gt;</span>Vanilla Sky<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">option</span>&gt;</span>Atomic Blonde<span class="tag">&lt;/<span class="name">option</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"submit"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">value</span>=<span class="string">"Submit"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>尽管这里的不同点很小，注意顶端的 <code>novalidate=&quot;true&quot;</code>。但是这很重要，因为浏览器会尝试在 <code>type=&quot;email&quot;</code> 的字端校验邮件地址。坦白说在这个案例中浏览器的校验规则是值得信任的，不过我们想要创建一个自定义校验的例子，所以把它禁用了。以下是更新后的 JavaScript。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  el: <span class="string">'#app'</span>,</span><br><span class="line">  data: &#123;</span><br><span class="line">    errors: [],</span><br><span class="line">    name: <span class="literal">null</span>,</span><br><span class="line">    email: <span class="literal">null</span>,</span><br><span class="line">    movie: <span class="literal">null</span></span><br><span class="line">  &#125;,</span><br><span class="line">  methods: &#123;</span><br><span class="line">    checkForm: <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">this</span>.errors = [];</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.name) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errors.push(<span class="string">"Name required."</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.email) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errors.push(<span class="string">'Email required.'</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="keyword">this</span>.validEmail(<span class="keyword">this</span>.email)) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errors.push(<span class="string">'Valid email required.'</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.errors.length) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      e.preventDefault();</span><br><span class="line">    &#125;,</span><br><span class="line">    validEmail: <span class="function"><span class="keyword">function</span> (<span class="params">email</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">var</span> re = <span class="regexp">/^(([^&lt;&gt;()\[\]\\.,;:\s@"]+(\.[^&lt;&gt;()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]&#123;1,3&#125;\.[0-9]&#123;1,3&#125;\.[0-9]&#123;1,3&#125;\.[0-9]&#123;1,3&#125;\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]&#123;2,&#125;))$/</span>;</span><br><span class="line">      <span class="keyword">return</span> re.test(email);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>如你所见，我们添加了一个新方法 <code>validEmail</code>，它将会在 <code>checkForm</code> 中被调用了。我们现在可以这样运行示例：</p>
<p></p><p data-height="265" data-theme-id="0" data-slug-hash="vWqNXZ" data-default-tab="html,result" data-user="cfjedimaster" data-embed-version="2" data-pen-title="form validation 2" class="codepen">在 <a href="https://codepen.io" target="_blank" rel="noopener">CodePen</a> 查看 Raymond Camden (<a href="https://codepen.io/cfjedimaster" target="_blank" rel="noopener">@cfjedimaster</a>) 的 <a href="https://codepen.io/cfjedimaster/pen/vWqNXZ/" target="_blank" rel="noopener">表单校验 2</a>。</p><p></p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

<h2 id="另一个自定义校验的例子"><a href="#另一个自定义校验的例子" class="headerlink" title="另一个自定义校验的例子"></a>另一个自定义校验的例子</h2><p>在第三个示例中，我们已经构建了一些你可能在一些调研类应用中见过的东西。用户需要花掉“预算”来为歼星舰模型装配一套部件。总价必须等于 100。先看 HTML。</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span></span></span><br><span class="line"><span class="tag">  <span class="attr">id</span>=<span class="string">"app"</span></span></span><br><span class="line"><span class="tag">  @<span class="attr">submit</span>=<span class="string">"checkForm"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">action</span>=<span class="string">"https://vuejs.org/"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">method</span>=<span class="string">"post"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">novalidate</span>=<span class="string">"true"</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"errors.length"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">b</span>&gt;</span>Please correct the following error(s):<span class="tag">&lt;/<span class="name">b</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ul</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">li</span> <span class="attr">v-for</span>=<span class="string">"error in errors"</span>&gt;</span>&#123;&#123; error &#125;&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    Given a budget of 100 dollars, indicate how much</span><br><span class="line">    you would spend on the following features for the</span><br><span class="line">    next generation Star Destroyer. Your total must sum up to 100.</span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model.number</span>=<span class="string">"weapons"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"number"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"weapons"</span></span></span><br><span class="line"><span class="tag">    &gt;</span> Weapons <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model.number</span>=<span class="string">"shields"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"number"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"shields"</span></span></span><br><span class="line"><span class="tag">    &gt;</span> Shields <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model.number</span>=<span class="string">"coffee"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"number"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"coffee"</span></span></span><br><span class="line"><span class="tag">    &gt;</span> Coffee <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model.number</span>=<span class="string">"ac"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"number"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"ac"</span></span></span><br><span class="line"><span class="tag">    &gt;</span> Air Conditioning <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model.number</span>=<span class="string">"mousedroids"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"number"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"mousedroids"</span></span></span><br><span class="line"><span class="tag">    &gt;</span> Mouse Droids <span class="tag">&lt;<span class="name">br</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    Current Total: &#123;&#123;total&#125;&#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"submit"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">value</span>=<span class="string">"Submit"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>这组输入框覆盖了五个不同的部件。注意这里为 <code>v-model</code> 特性添加了 <code>.number</code>。它会告诉 Vue 将其值作为数字来使用。不过这里有一个小小的 bug，那就是当其值为空的时候，它会回到字符串格式，稍后你将会看到变通的办法。为了让用户使用起来更方便，我们添加展示了一个当前的总和，这样我们就能够实时的看到它们一共花掉了多少钱。现在我们来看看 JavaScript。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  el: <span class="string">'#app'</span>,</span><br><span class="line">  data:&#123;</span><br><span class="line">    errors: [],</span><br><span class="line">    weapons: <span class="number">0</span>,</span><br><span class="line">    shields: <span class="number">0</span>,</span><br><span class="line">    coffee: <span class="number">0</span>,</span><br><span class="line">    ac: <span class="number">0</span>,</span><br><span class="line">    mousedroids: <span class="number">0</span></span><br><span class="line">  &#125;,</span><br><span class="line">  computed: &#123;</span><br><span class="line">     total: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">       <span class="comment">// 必须解析，因为 Vue 会将空值转换为字符串</span></span><br><span class="line">       <span class="keyword">return</span> <span class="built_in">Number</span>(<span class="keyword">this</span>.weapons) +</span><br><span class="line">         <span class="built_in">Number</span>(<span class="keyword">this</span>.shields) +</span><br><span class="line">         <span class="built_in">Number</span>(<span class="keyword">this</span>.coffee) +</span><br><span class="line">         <span class="built_in">Number</span>(<span class="keyword">this</span>.ac+<span class="keyword">this</span>.mousedroids);</span><br><span class="line">     &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  methods:&#123;</span><br><span class="line">    checkForm: <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">this</span>.errors = [];</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.total != <span class="number">100</span>) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errors.push(<span class="string">'Total must be 100!'</span>);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (!<span class="keyword">this</span>.errors.length) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      e.preventDefault();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>我们将总和设置为了一个计算属性，从那个我们解决掉的 bug 外面看上去，这已经足够了。我的 <code>checkForm</code> 方法现在只需要关注总和是不是 100 了。你可以在这里试用：</p>
<p></p><p data-height="265" data-theme-id="0" data-slug-hash="vWqGoy" data-default-tab="html,result" data-user="cfjedimaster" data-embed-version="2" data-pen-title="form validation 3" class="codepen">在 <a href="https://codepen.io" target="_blank" rel="noopener">CodePen</a> 查看 Raymond Camden (<a href="https://codepen.io/cfjedimaster" target="_blank" rel="noopener">@cfjedimaster</a>) 的 <a href="https://codepen.io/cfjedimaster/pen/vWqGoy/" target="_blank" rel="noopener">表单校验3</a>。</p><p></p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

<h2 id="服务端校验"><a href="#服务端校验" class="headerlink" title="服务端校验"></a>服务端校验</h2><p>在我们最终的示例中，我们构建了一些用到 Ajax 的服务端校验的东西。这个表单将会问你为一个新产品起名字，并且将会确保这个名字是唯一的。我们快速写了一个 <a href="http://openwhisk.apache.org/" target="_blank" rel="noopener">OpenWhisk</a> 的 serverless action 来进行这个校验。虽然这不是非常重要，但其逻辑如下：</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">main</span>(<span class="params">args</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 不好的产品名：vista, empire, mbp</span></span><br><span class="line">    <span class="keyword">const</span> badNames = [<span class="string">'vista'</span>, <span class="string">'empire'</span>, <span class="string">'mbp'</span>];</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span> (badNames.includes(args.name)) &#123;</span><br><span class="line">      reject(&#123;<span class="attr">error</span>: <span class="string">'Existing product'</span>&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    resolve(&#123;<span class="attr">status</span>: <span class="string">'ok'</span>&#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>基本上除了“vista”、“empire”和“mbp”的名字都是可以接受的。好，让我们来看看表单。</p>
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">form</span></span></span><br><span class="line"><span class="tag">  <span class="attr">id</span>=<span class="string">"app"</span></span></span><br><span class="line"><span class="tag">  @<span class="attr">submit</span>=<span class="string">"checkForm"</span></span></span><br><span class="line"><span class="tag">  <span class="attr">method</span>=<span class="string">"post"</span></span></span><br><span class="line"><span class="tag">&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span> <span class="attr">v-if</span>=<span class="string">"errors.length"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">b</span>&gt;</span>Please correct the following error(s):<span class="tag">&lt;/<span class="name">b</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ul</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">li</span> <span class="attr">v-for</span>=<span class="string">"error in errors"</span>&gt;</span>&#123;&#123; error &#125;&#125;<span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"name"</span>&gt;</span>New Product Name: <span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">id</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">v-model</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"text"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">name</span>=<span class="string">"name"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">input</span></span></span><br><span class="line"><span class="tag">      <span class="attr">type</span>=<span class="string">"submit"</span></span></span><br><span class="line"><span class="tag">      <span class="attr">value</span>=<span class="string">"Submit"</span></span></span><br><span class="line"><span class="tag">    &gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>这里没有任何特殊的东西。接下来我们再看看 JavaScript。</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> apiUrl = <span class="string">'https://openwhisk.ng.bluemix.net/api/v1/web/rcamden%40us.ibm.com_My%20Space/safeToDelete/productName.json?name='</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">  el: <span class="string">'#app'</span>,</span><br><span class="line">  data: &#123;</span><br><span class="line">    errors: [],</span><br><span class="line">    name: <span class="string">''</span></span><br><span class="line">  &#125;,</span><br><span class="line">  methods:&#123;</span><br><span class="line">    checkForm: <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>&#123;</span><br><span class="line">      e.preventDefault();</span><br><span class="line"></span><br><span class="line">      <span class="keyword">this</span>.errors = [];</span><br><span class="line"></span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.name === <span class="string">''</span>) &#123;</span><br><span class="line">        <span class="keyword">this</span>.errors.push(<span class="string">'Product name is required.'</span>);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        fetch(apiUrl + <span class="built_in">encodeURIComponent</span>(<span class="keyword">this</span>.name))</span><br><span class="line">        .then(<span class="function"><span class="params">res</span> =&gt;</span> res.json())</span><br><span class="line">        .then(<span class="function"><span class="params">res</span> =&gt;</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (res.error) &#123;</span><br><span class="line">            <span class="keyword">this</span>.errors.push(res.error);</span><br><span class="line">          &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 在成功的时候重定向到一个新的 URL 或做一些别的事情</span></span><br><span class="line">            alert(<span class="string">'ok!'</span>);</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>我们从一个运行在 OpenWhisk 的 API 的 URL 变量开始。现在注意 <code>checkForm</code>。在这个版本中，我们始终阻止了表单的提交 (当然，它也可以通过 Vue 在 HTML 中完成)。你可以看到一个基本的校验，即 <code>this.name</code> 是否为空，然后我们请求这个 API。如果名字是无效的，我们就添加一个错误。如果是有效的，我们就不做任何事 (只是一个 alert)，但是你可以引导用户去一个新页面，在 URL 中带上产品的名字，或者其它行为。接下来你可以体验这个 demo：</p>
<p></p><p data-height="265" data-theme-id="0" data-slug-hash="BmgzeM" data-default-tab="js,result" data-user="cfjedimaster" data-embed-version="2" data-pen-title="form validation 4" class="codepen">在 <a href="https://codepen.io" target="_blank" rel="noopener">CodePen</a> 查看 Raymond Camden (<a href="https://codepen.io/cfjedimaster" target="_blank" rel="noopener">@cfjedimaster</a>) 的 <a href="https://codepen.io/cfjedimaster/pen/BmgzeM/" target="_blank" rel="noopener">表单校验4。</a></p><p></p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

<h2 id="其它替代模式"><a href="#其它替代模式" class="headerlink" title="其它替代模式"></a>其它替代模式</h2><p>这份秘笈专注在“手动”校验表单，当然一些非常棒的 Vue 的库会为你搞定这些事情。使用一些预打包的库可能会影响你的应用最终的体积，但是好处是非常多的。这里有经过充分测试且保持日常更新的代码。其中包括以下 Vue 的表单校验库：</p>
<ul>
<li><a href="https://github.com/monterail/vuelidate" target="_blank" rel="noopener">vuelidate</a></li>
<li><a href="http://vee-validate.logaretm.com/" target="_blank" rel="noopener">VeeValidate</a></li>
</ul>

  
  
</div>

        
      </div>
      <script src="/netinfofe/js/smooth-scroll.min.js"></script>
    

    <!-- main custom script for sidebars, version selects etc. -->
    <script src="/netinfofe/js/css.escape.js"></script>
    <script src="/netinfofe/js/common.js"></script>

    <!-- search -->
    <link href="//cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css" rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="/netinfofe/css/search.css">
    <script src="//cdn.jsdelivr.net/docsearch.js/1/docsearch.min.js"></script>
    <script>
    [
      '#search-query-nav',
      '#search-query-sidebar',
      '#search-query-menu'
    ].forEach(function (selector) {
      if (!document.querySelector(selector)) return
      // search index defaults to v2
      var match = window.location.pathname.match(/^\/(v\d+)/)
      var version = match ? match[1] : 'v2'
      docsearch({
      appId: 'BH4D9OD16A',
      apiKey: '5638280abff9d207417bb03be05f0b25',
      indexName: 'vuejs_cn2',
      inputSelector: selector,
      algoliaOptions: { facetFilters: ["version:" + version] }
      })
    })
    </script>

    <!-- fastclick -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.6/fastclick.min.js"></script>
    <script>
    document.addEventListener('DOMContentLoaded', function() {
      FastClick.attach(document.body)
    }, false)
    </script>
  </body>
</html>
